package ${groupId}.service.security;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.shiro.authz.ModularRealmAuthorizer;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.lang.codec.Base64;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.ServletContainerSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.env.Environment;

import ${groupId}.service.security.credential.RetryLimitHashedCredentialsMatcher;
import ${groupId}.service.security.dto.ShiroConfigDto;
import ${groupId}.service.security.filter.KickoutSessionFilter;
import ${groupId}.service.security.filter.MallFormAuthenticationFilter;
import ${groupId}.service.security.pam.MallModularRealmAuthenticator;
import ${groupId}.service.security.realm.BossUserRealmImpl;
import ${groupId}.service.security.realm.MerchantUserRealmImpl;

import jakarta.servlet.Filter;

@Configuration
class ShiroConfiguration {
	private static Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();

	private static final String bossPath = "/boss";
	private static final String merchantPath = "/merchant";
	@Autowired
	private ShiroConfigDto shiroConfigDto;
	@Autowired
	private Environment environment;
	
	/**
	 * boss账号登录实现
	 * @return
     */
	@Bean(name = "bossUserRealmImpl")
	BossUserRealmImpl bossUserRealm() {
		BossUserRealmImpl bossUserRealm = new BossUserRealmImpl();
		bossUserRealm.setCredentialsMatcher(credentialsMatcher());
		bossUserRealm.setAuthenticationCacheName("authenticationCache");
		bossUserRealm.setAuthorizationCacheName("authorizationCache");
		bossUserRealm.setAuthenticationCachingEnabled(false);
		bossUserRealm.setAuthorizationCachingEnabled(false);
		bossUserRealm.setCachingEnabled(true);
		bossUserRealm.setCacheManager(getEhCacheManager());
		bossUserRealm.setSupportedLoginType("boss");
		return bossUserRealm;
	}

	/**
	 * 商户账号登录实现
	 * @return
     */
	@Bean(name = "merchantUserRealmImpl")
	MerchantUserRealmImpl merchantUserRealm(){
		MerchantUserRealmImpl merchantUserRealm = new MerchantUserRealmImpl();
		merchantUserRealm.setCredentialsMatcher(credentialsMatcher());
		merchantUserRealm.setAuthenticationCacheName("authenticationCache");
		merchantUserRealm.setAuthorizationCacheName("authorizationCache");
		merchantUserRealm.setAuthenticationCachingEnabled(false);
		merchantUserRealm.setAuthorizationCachingEnabled(false);
		merchantUserRealm.setCachingEnabled(true);
		merchantUserRealm.setCacheManager(getEhCacheManager());
		merchantUserRealm.setSupportedLoginType("merchant");
		return merchantUserRealm;
	}

	/**
	 * 凭证匹配器
	 * @return
	 */
	@Bean(name = "authenticator")
	MallModularRealmAuthenticator authenticator(){
		MallModularRealmAuthenticator authenticator = new MallModularRealmAuthenticator();
		List<Realm> realms = new ArrayList<>();
		realms.add(bossUserRealm());
		realms.add(merchantUserRealm());
		authenticator.setRealms(realms);
		return authenticator;
	}

	/**
	 * 权限匹配器
	 * @return
	 */
	@Bean(name = "authorizer")
	ModularRealmAuthorizer authorizer(){
		ModularRealmAuthorizer authorizer = new ModularRealmAuthorizer();
		List<Realm> realms = new ArrayList<>();
		realms.add(bossUserRealm());
		realms.add(merchantUserRealm());
		authorizer.setRealms(realms);
		return authorizer;
	}

	/**
	 * 安全管理器
	 * @return
     */
	@Bean(name = "securityManager")
	DefaultWebSecurityManager getDefaultWebSecurityManager() {
		DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
		defaultWebSecurityManager.setCacheManager(getEhCacheManager());
		defaultWebSecurityManager.setSessionManager(defaultWebSessionManager());
		defaultWebSecurityManager.setAuthenticator(authenticator());
		defaultWebSecurityManager.setAuthorizer(authorizer());
		defaultWebSecurityManager.setRememberMeManager(cookieRememberMeManager());
		return defaultWebSecurityManager;
	}

	@Bean(name = "shiroEhcacheManager")
	EhCacheManager getEhCacheManager() {
		EhCacheManager ehCacheManager = new EhCacheManager();
		ehCacheManager.setCacheManagerConfigFile("classpath:ehcache/ehcache.xml");
		return ehCacheManager;
	}

	/**
	@Bean(name="sessionManager")
	DefaultWebSessionManager defaultWebSessionManager() {
		DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
		sessionManager.setCacheManager(getEhCacheManager());
		sessionManager.setGlobalSessionTimeout(shiroConfigDto.getGlobalSessionTimeout()*60*1000);
		sessionManager.setDeleteInvalidSessions(true);
		sessionManager.setSessionValidationSchedulerEnabled(true);
		sessionManager.setDeleteInvalidSessions(true);
		sessionManager.setSessionIdCookieEnabled(true);
		sessionManager.setSessionIdCookie(simpleCookie());
		sessionManager.setSessionDAO(sessionDAO());
		sessionManager.setSessionIdUrlRewritingEnabled(false);
		return sessionManager;
	}
	*/
	
	@Bean(name="sessionManager")
	ServletContainerSessionManager defaultWebSessionManager() {
		ServletContainerSessionManager servletContainerSessionManager = new ServletContainerSessionManager();
		return servletContainerSessionManager;
	}
	
	@Bean
	CookieRememberMeManager cookieRememberMeManager() {
	    CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
	    SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
	    simpleCookie.setHttpOnly(true);
	    //设置RememberMe的cookie有效期为7天
	    simpleCookie.setMaxAge(shiroConfigDto.getRememberMeMaxAge()*60*60);
	    cookieRememberMeManager.setCookie(simpleCookie);
	    cookieRememberMeManager.setCipherKey(Base64.decode("6ZmI6I2j3Y+R1aSn5BOlAA=="));
	    return cookieRememberMeManager;
	}


	@Bean(name = "simpleCookie")
	SimpleCookie simpleCookie(){
		SimpleCookie simpleCookie = new SimpleCookie("CMF-MALL-ID");
		simpleCookie.setHttpOnly(true);
		simpleCookie.setMaxAge(-1);
		return simpleCookie;
	}

	@Bean(name = "lifecycleBeanPostProcessor")
	static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
		return new LifecycleBeanPostProcessor();
	}

	@Bean
	@DependsOn("lifecycleBeanPostProcessor")
	DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
		daap.setProxyTargetClass(true);
		return daap;
	}

	/**
	 * 密码重试限制器
	 * @return
	 */
	@Bean
	RetryLimitHashedCredentialsMatcher credentialsMatcher(){
		RetryLimitHashedCredentialsMatcher credentialsMatcher = new RetryLimitHashedCredentialsMatcher(getEhCacheManager());
		//加密方式
		credentialsMatcher.setHashAlgorithmName("SHA-256");
		//加密次数
		credentialsMatcher.setHashIterations(1);
		//存储散列后的密码是否为16进制
		credentialsMatcher.setStoredCredentialsHexEncoded(true);
		return credentialsMatcher;
	}

	@Bean
	JavaUuidSessionIdGenerator sessionIdGenerator(){
		return new JavaUuidSessionIdGenerator();
	}

	@Bean
	EnterpriseCacheSessionDAO sessionDAO(){
		EnterpriseCacheSessionDAO sessionDAO = new EnterpriseCacheSessionDAO();
		sessionDAO.setActiveSessionsCacheName("shiro-activeSessionCache");
		sessionDAO.setSessionIdGenerator(sessionIdGenerator());
		return sessionDAO;
	}

	@Bean
	AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {
		AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
		aasa.setSecurityManager(getDefaultWebSecurityManager());
		return new AuthorizationAttributeSourceAdvisor();
	}

	@Bean(name = "shiroFilter")
	ShiroFilterFactoryBean getShiroFilterFactoryBean() {
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		shiroFilterFactoryBean.setSecurityManager(getDefaultWebSecurityManager());
		/*设置登录地址*/
		shiroFilterFactoryBean.setLoginUrl("/login");
		// 全局的没啥用，在filter里面有单独设置
		// shiroFilterFactoryBean.setUnauthorizedUrl("/error");
		/* 过滤器 */
		Map<String,Filter> filters = new HashMap<>();
		/*
		 *  认证filter
		 *  分别集成自：
		 *  	FormAuthenticationFilter
		 *  	AccessControlFilter
		 *  
		 */
		filters.put("authc", getFormAuthenticationFilter());
		filters.put("kickout", getKickoutSessionFilter());
		/**
		 * 登出filter
		 * 直接使用系统LogoutFilter
		 */
		filters.put("bossLogoutFilter", getBossLogoutFilter());
		filters.put("merchantLogoutFilter", getMerchantLogoutFilter());
		shiroFilterFactoryBean.setFilters(filters);
		/* 
		 * 设置URL权限
		 * */
		filterChainDefinitionMap.put("/components/**", "anon");
		filterChainDefinitionMap.put("/*/captcha*", "anon");
		filterChainDefinitionMap.put(bossPath+"/login", "authc");
		filterChainDefinitionMap.put(merchantPath+"/login", "authc");
		filterChainDefinitionMap.put(bossPath+"/logout", "bossLogoutFilter");
		filterChainDefinitionMap.put(merchantPath+"/logout", "merchantLogoutFilter");
		filterChainDefinitionMap.put(bossPath+"/**", "kickout");
		filterChainDefinitionMap.put(merchantPath+"/**", "kickout");
		
		if(environment.getActiveProfiles()[0].equalsIgnoreCase("prod")) {
			// 使用kickout filter，FormAuthenticationFilter有问题，原因暂时未知
			filterChainDefinitionMap.put("/doc.html*", "kickout");
		}
		
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		return shiroFilterFactoryBean;
	}

	MallFormAuthenticationFilter getFormAuthenticationFilter(){
		MallFormAuthenticationFilter filter = new MallFormAuthenticationFilter();
		filter.setUsernameParam("userName");
		filter.setPasswordParam("password");
		filter.setLoginTypeParamName("loginType");
		filter.setCaptchaParamName("captcha");
		filter.setBossPath(bossPath);
		filter.setMerchantPath(merchantPath);
		return filter;
	}

	KickoutSessionFilter getKickoutSessionFilter(){
		KickoutSessionFilter filter = new KickoutSessionFilter();
		filter.setBossPath(bossPath);
		filter.setMerchantPath(merchantPath);
		return filter;
	}

	LogoutFilter getBossLogoutFilter(){
		LogoutFilter logoutFilter = new LogoutFilter();
		logoutFilter.setRedirectUrl(bossPath+"/login");
		return logoutFilter;
	}

	LogoutFilter getMerchantLogoutFilter(){
		LogoutFilter logoutFilter = new LogoutFilter();
		logoutFilter.setRedirectUrl(merchantPath+"/login");
		return logoutFilter;
	}
}